package com.hhd.flow.engine.cmd.impl;

import com.alibaba.fastjson.JSONObject;
import com.hhd.flow.engine.cmd.Command;
import com.hhd.flow.engine.cmd.PassTaskContext;
import com.hhd.flow.engine.utils.CommonUtils;
import com.hhd.flow.engine.utils.FlowUtils;
import com.hhd.flow.engine.variable.FlowVariablesContainer;
import com.hhd.flow.engine.variable.impl.CompositeFlowVariablesContainer;
import com.hhd.flow.engine.variable.impl.JSONFlowVariablesContainer;
import com.hhd.flow.engine.vo.FlowModel;
import com.hhd.flow.entity.*;
import com.hhd.flow.exception.FlowException;
import com.hhd.flow.service.IFlowTaskService;
import lombok.experimental.SuperBuilder;

import java.util.List;
import java.util.Objects;

@SuperBuilder(toBuilder = true)
public abstract class AbstractCommand<T> implements Command<T> {
    @Override
    public T execute() {
        return null;
    }

    protected void loadContextData(PassTaskContext context) {
        //加载流程实例
        FlowProcessInstance flowProcessInstance = loadFlowProcessInstance(context);

        //加载流程定义
        loadFlowProcessDefinitionRef(context, flowProcessInstance);

        //加载任务实例
        loadFlowTask(context);

        //加载路径实例
        loadFlowPath(context);
    }

    protected void loadFlowPath(PassTaskContext context) {
        FlowPath path = loadFlowPathByProcessInstance(context.getFlowProcessInstance());
        context.setPath(path);
    }

    protected FlowPath loadFlowPathByProcessInstance(FlowProcessInstance flowProcessInstance) {
        List<FlowPath> flowPaths = getEngine().getFlowPathService().lambdaQuery().eq(FlowPath::getProcessInstanceId, flowProcessInstance.getId()).list();
        FlowPath path = null;
        if (!CommonUtils.isEmpty(flowPaths)) {
            path = flowPaths.get(0);
        } else {
            path = FlowUtils.createFlowPath(flowProcessInstance);
            getEngine().getFlowPathService().save(path);
        }
        return path;
    }

    protected void loadFlowTask(PassTaskContext context) {
        IFlowTaskService flowTaskService = getEngine().getFlowTaskService();
        FlowTask flowTask = flowTaskService.getById(context.getFlowTaskId());
        if (Objects.isNull(flowTask)) throw new FlowException("没有找到任务: " + context.getFlowTaskId());
        context.setFlowTask(flowTask);
    }

    protected void loadFlowProcessDefinitionRef(PassTaskContext context, FlowProcessInstance flowProcessInstance) {
        if (!CommonUtils.isEmpty(flowProcessInstance.getModel())) {
            context.setFlowModel(getModel(flowProcessInstance));
            return;
        }
        List<FlowProcessDefinitionRef> list = getEngine().getFlowProcessDefinitionRefService().lambdaQuery()
                .eq(FlowProcessDefinitionRef::getId, flowProcessInstance.getProcessDefinitionId())
                .list();
        if (list.isEmpty()) throw new FlowException("没有找到流程定义引用");
        context.setFlowProcessDefinitionRef(list.get(0));
        context.setFlowModel(getModel(context.getFlowProcessDefinitionRef()));
    }

    protected FlowProcessInstance loadFlowProcessInstance(PassTaskContext context) {
        FlowProcessInstance flowProcessInstance = getEngine().getFlowProcessInstanceService().getById(context.getFlowProcessInstanceId());
        if (Objects.isNull(flowProcessInstance))
            throw new FlowException("没有找到流程: " + context.getFlowProcessInstanceId());
        context.setFlowProcessInstance(flowProcessInstance);
        return flowProcessInstance;
    }

    protected FlowModel loadFlowModelByProcessInstanceId(String processInstanceId) {
        String processDefinitionId = null;
        FlowProcessInstance flowProcessInstance = getEngine().getFlowProcessInstanceService().getById(processInstanceId);
        if (!CommonUtils.isEmpty(flowProcessInstance) && !CommonUtils.isEmpty(flowProcessInstance.getModel()))
            return getModel(flowProcessInstance);
        FlowProcessInstanceHistory processInstanceHistory = getEngine().getFlowProcessInstanceHistoryService().getById(processInstanceId);
        if (!CommonUtils.isEmpty(processInstanceHistory) && !CommonUtils.isEmpty(processInstanceHistory.getModel()))
            return getModel(processInstanceHistory);

        if (!CommonUtils.isEmpty(flowProcessInstance)) {
            processDefinitionId = flowProcessInstance.getProcessDefinitionId();
        }
        if (!CommonUtils.isEmpty(processInstanceHistory)) {
            processDefinitionId = processInstanceHistory.getProcessDefinitionId();
        }
        CommonUtils.isEmptyThrow(processInstanceId, new RuntimeException("没有找到流程实例：" + processInstanceId));
        FlowProcessDefinitionRef flowProcessDefinitionRef = loadFlowProcessDefinitionRef(processDefinitionId);
        CommonUtils.isEmptyThrow(processInstanceId, new RuntimeException("没有找到流程实例定义引用：" + processDefinitionId));
        return getModel(flowProcessDefinitionRef);
    }

    protected FlowProcessDefinitionRef loadFlowProcessDefinitionRef(String processDefinitionId) {
        return getEngine().getFlowProcessDefinitionRefService().getById(processDefinitionId);
    }

    private FlowModel getModel(FlowProcessInstanceHistory processInstanceHistory) {
        return FlowModel.forString(processInstanceHistory.getModel());
    }

    protected FlowModel getModel(FlowProcessDefinitionRef flowProcessDefinitionRef) {
        return FlowModel.forString(flowProcessDefinitionRef.getModel());
    }

    protected FlowModel getModel(FlowProcessInstance flowProcessInstance) {
        return FlowModel.forString(flowProcessInstance.getModel());
    }

    protected FlowVariablesContainer getVariablesContainer(String formTypeId, String formId, FlowProcessInstance flowProcessInstance, JSONObject formModel) {
        CompositeFlowVariablesContainer res = new CompositeFlowVariablesContainer();
        if (!CommonUtils.isEmpty(formModel)) {
            res.add(getEngine().convertJSONToFormModel(formTypeId, formId, flowProcessInstance, formModel));
        }
        if (!CommonUtils.isEmpty(formId)) {
            res.add(getEngine().loadVariables(formTypeId, formId, flowProcessInstance));
        }
        return res;
    }
}
